#ifndef COMPOSITE_IMAGE_H_
#define COMPOSITE_IMAGE_H_

// Base classes.
#include <boost/noncopyable.hpp>
#include "CompositeImageInterface.h"
// Members.
#include <boost/scoped_array.hpp>
#include "Color.h"

#include <vector>
#include <cmath>
#include "CompositableImage.h"

// T must implement SimpleImage.
template<class T>
class CompositeImage
    : public CompositeImageInterface,
      private boost::noncopyable {
  public:
    // Constructs an empty composite image.
    CompositeImage();
    // Constructs a composite image of known size.
    CompositeImage(int width, int height, int tile_width, int tile_height,
        const Color& background);

    // Implementation of ReadableImage interface.
    int width() const;
    int height() const;
    Color pixel(int i, int j) const;

    // Resets a composite image.
    void reset(int width, int height, int tile_width, int tile_height,
        const Color& background);

    // Loads an image from raw data.
    int loadTileFromStream(int i, int j, std::istream& stream);

    // Flatten the tiles down into an image.
    void flatten(CompositableImage<T>* output) const;

  private:
    // Image dimensions.
    int width_;
    int height_;
    // T dimensions.
    int tile_width_;
    int tile_height_;
    // Size of tile grid.
    int rows_;
    int cols_;
    // Background color if a tile is not set.
    Color background_;
    // 2D grid of images.
    boost::scoped_array<T> images_;

    // Accesses one tile of a composite image.
    T* tile(int i, int j);
    const T* tile(int i, int j) const;
};


// Construct an empty composite image.
template<class T>
CompositeImage<T>::CompositeImage()
    : width_(0),
      height_(0),
      tile_width_(0),
      tile_height_(0),
      rows_(0),
      cols_(0),
      background_(0, 0, 0),
      images_() {}

// Construct a composite image of known size.
template<class T>
CompositeImage<T>::CompositeImage(int width, int height, int tile_width,
                                  int tile_height, const Color& background)
    : width_(width),
      height_(height),
      tile_width_(tile_width),
      tile_height_(tile_height),
      rows_(0),
      cols_(0),
      background_(background),
      images_() {
  // Calculate number of rows and columns.
  rows_ = ceil((double)height_ / tile_height_);
  cols_ = ceil((double)width_ / tile_width_);

  // Populate with empty tiles.
  images_.reset(new T[rows_ * cols_]);
}

// Reset a composite image.
template<class T>
void CompositeImage<T>::reset(int width, int height, int tile_width,
                              int tile_height, const Color& background) {
  width_ = width;
  height_ = height;
  tile_width_ = tile_width;
  tile_height_ = tile_height;
  background_ = background;

  // Calculate number of rows and columns.
  rows_ = ceil((double)height_ / tile_height_);
  cols_ = ceil((double)width_ / tile_width_);

  // Populate with empty tiles.
  images_.reset(new T[rows_ * cols_]);
}

// Loads an image from raw data.
template<class T>
int CompositeImage<T>::loadTileFromStream(int i, int j, std::istream& stream) {
  return tile(i, j)->loadFromStream(stream);
}

template<class T>
int CompositeImage<T>::width() const {
  return width_;
}

template<class T>
int CompositeImage<T>::height() const {
  return height_;
}

template<class T>
Color CompositeImage<T>::pixel(int i, int j) const {
  int u = i / tile_height_;
  int v = j / tile_width_;
  int a = i % tile_height_;
  int b = j % tile_width_;

  // Access the pixel within the tile.
  return tile(u, v)->pixel(a, b);
}

// Accesses one tile of a composite image.
template<class T>
T* CompositeImage<T>::tile(int i, int j) {
  return &images_[i * cols_ + j];
}

template<class T>
const T* CompositeImage<T>::tile(int i, int j) const {
  return &images_[i * cols_ + j];
}

// Flatten the tiles down into an image.
template<class T>
void CompositeImage<T>::flatten(CompositableImage<T>* output) const {
  // Create a new blank image.
  output->create(width_, height_, Color(0, 0, 0));

  // Composite each tile on to the image.
  for (int i = 0; i < rows_; i++) {
    for (int j = 0; j < cols_; j++) {
      int u = i * tile_height_;
      int v = j * tile_width_;

      output->composite(*tile(i, j), u, v);
    }
  }
}

#endif
